﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleDBase
{
    class Table
    {
        public string Name { set; get; }
        public List<Field> TableFields { get; private set; }
        public List<List<Data>> Records { get; private set; }
        public int NumOfRecords { get; private set; }

        public Table()
        {
            TableFields = new List<Field>();
            Records = new List<List<Data>>();
            NumOfRecords = 0;
        }

        public Table(string file)
        {
            TableFields = new List<Field>();
            Records = new List<List<Data>>();

            FileInfo fi = new FileInfo(file);

            using (StreamReader sr = fi.OpenText())
            {
                string input = null;
                Name = sr.ReadLine();
                int fieldnum = Int32.Parse(sr.ReadLine());
                
                //determine fields
                for (int i = 0; i < fieldnum; ++i)
                {
                    input = sr.ReadLine();
                    string[] datas = input.Split(new char[] { '\t' });
                    UploadField(datas);
                }

                //determine records
                while (!sr.EndOfStream)
                {
                    string line = sr.ReadLine();
                    string[] datas = line.Split(new char[] { '\t' });
                    UploadRecord(datas);
                }
            }
        }

        private void UploadField(string[] datas)
        {
            bool nullable = false;
            switch (datas[2])
            {
                case "nullable": nullable = true; break;
                case "notnullable": nullable = false; break;
            }


            switch (datas[1])
            {
                case "Integer":
                    {
                        TableFields.Add(new Field(datas[0], "Integer", nullable));
                    } break;
                case "Real":
                    {
                        TableFields.Add(new Field(datas[0], "Real", nullable));
                    } break;
                case "Boolean":
                    {
                        TableFields.Add(new Field(datas[0], "Boolean", nullable));
                    } break;
                case "String":
                    {
                        TableFields.Add(new Field(datas[0], "String", nullable));
                    } break;
            }
        }

        public void UploadField(string name, string type, bool isnullable)
        {
            TableFields.Add(new Field(name, type, isnullable));

            for (int i = 0; i < Records.Count; ++i)
            {
                switch (type)
                {
                    case "Integer": Records[i].Add(new Integer(0, true)); break;
                    case "Real": Records[i].Add(new Real(0, true)); break;
                    case "String": Records[i].Add(new String("", true)); break;
                    case "Boolean": Records[i].Add(new Boolean(false, true)); break;
                }
            }
        }

        public void UploadRecord(string[] datas)
        {
            List<Data> newrec = new List<Data>();

            for (int i = 0; i < datas.Length; ++i)
            {
                switch(TableFields[i].Type)
                {
                    case "Integer": 
                        {
                            if (datas[i] == "null") newrec.Add(new Integer(0, true));
                                else newrec.Add(new Integer(Int32.Parse(datas[i])));
                        } break;
                    case "Real": 
                        {
                            if (datas[i] == "null") newrec.Add(new Real(0, true));
                                else newrec.Add(new Real(Double.Parse(datas[i]))); 
                        } break;
                    case "Boolean": 
                        {
                            if (datas[i] == "null") newrec.Add(new Boolean(false, true));
                                else newrec.Add(new Boolean(bool.Parse(datas[i]))); 
                        } break;
                    case "String": 
                        {
                            if (datas[i] == "null") newrec.Add(new String("", true));
                                else newrec.Add(new String(datas[i])); 
                        } break;
                }
            }

            Records.Add(newrec);
            NumOfRecords++;
        }

        public void UploadRecord(List<Data> datas)
        {
            Records.Add(datas);
            NumOfRecords++;
        }

        public void DeleteField(string name)
        {
            int place = 0;
            while (place <= TableFields.Count - 1 && TableFields[place].Name != name)
            {
                ++place;
            }

            if (place <= TableFields.Count - 1)
            {
                TableFields.RemoveAt(place);

                for (int i = 0; i < Records.Count; ++i)
                {
                    Records[i].RemoveAt(place);
                }
            }
        }

        private int FieldMaxWidth(int field)
        {
            int ret = Records[0][field].GetValue().Length;
            for (int i = 1; i < Records.Count; ++i)
            {
                int currlength = Records[i][field].GetValue().Length;
                if (currlength > ret) ret = currlength;
            }

            return ret;
        }

        public void PrintTable(int cursorleft, int cursortop, List<int> recs = null)
        {
            Console.SetCursorPosition(cursorleft, cursortop);

            //width of fields
            List<int> fieldwidths = new List<int>();
            //Console.SetCursorPosition(0, 10);

            for (int i = 0; i < TableFields.Count; ++i)
            {
                int recordwidth = 0;
                if (NumOfRecords > 0) recordwidth = FieldMaxWidth(i) + 2;

                int fieldname = TableFields[i].Name.Length + 2;
                if (recordwidth > fieldname)
                {
                    fieldwidths.Add(recordwidth);
                }
                else
                {
                    fieldwidths.Add(fieldname);
                }
            }

            //***** print header
            int totalwidth = 0;
            for (int i = 0; i < fieldwidths.Count; ++i) totalwidth += fieldwidths[i];
            totalwidth += TableFields.Count + 1;

            //positioning to center and optionally resize window
            if (totalwidth + 2 > Console.WindowWidth)
            {
                Console.SetWindowSize(totalwidth + 2, Console.WindowHeight);
            }

            cursorleft = (Console.WindowWidth - totalwidth) / 2;

            //header top and bottom border + separating lines + field names
            for (int i = 0; i < totalwidth; ++i)
            {
                Console.SetCursorPosition(cursorleft + i, cursortop);
                Console.Write("_");
                Console.SetCursorPosition(cursorleft + i, cursortop + 2);
                Console.Write("_");
            }

            //field names
            int x = 0;
            for (int i = 0; i < TableFields.Count; ++i )
            {
                Console.SetCursorPosition(cursorleft + x, cursortop + 1);
                Console.Write("| " + TableFields[i].Name + " ");
                x += fieldwidths[i] + 1;
            }

            Console.SetCursorPosition(cursorleft + totalwidth - 1, cursortop + 1);
            Console.Write("|");

            //***** print records
            int row = 0;

            for (int i = 0; i < Records.Count; ++i)
            {
                x = 0;

                if (recs == null || (recs != null && recs.Contains(i)))
                {
                    for (int j = 0; j < Records[i].Count; ++j)
                    {
                        Console.SetCursorPosition(cursorleft + x, cursortop + 3 + row);

                        if (Records[i][j].IsNull)
                        {
                            Console.Write("| - ");
                        }
                        else
                        {
                            Console.Write("| " + Records[i][j].GetValue() + " ");
                        }

                        x += fieldwidths[j] + 1;
                    }

                    Console.SetCursorPosition(cursorleft + totalwidth - 1, cursortop + 3 + row);
                    Console.Write("|");

                    ++row;
                }
            }
        }

        public void DeleteRecords(List<int> delete)
        {
            List<List<Data>> newRecords = new List<List<Data>>();

            for (int i = 0; i < Records.Count; ++i)
            {
                if (!delete.Contains(i))
                    newRecords.Add(Records[i]);
            }

            Records = newRecords;
            NumOfRecords = newRecords.Count;
        }

        public void FlushContent()
        {
            Records.Clear();
            NumOfRecords = 0;
        }
    }
}
